package com.woniu.service.impl;

import com.rabbitmq.client.Channel;
import com.woniu.cinema.domain.HallMovie;
import com.woniu.common.constant.UserConstants;
import com.woniu.common.enums.ExceptionCode;
import com.woniu.common.exception.CustomException;
import com.woniu.dto.OrderDto;
import com.woniu.feign.CinemaFeignClient;
import com.woniu.mapper.OrderMapper;
import com.woniu.mapper.OrderSeatMapper;
import com.woniu.order.Order;
import com.woniu.order.OrderSeat;
import com.woniu.service.OrderService;

import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import tk.mybatis.mapper.entity.Example;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author 杨光 微信号:tolryg
 * @time 14:27
 */
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private CinemaFeignClient cinemaFeignClient;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderSeatMapper orderSeatMapper;


    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Autowired
    private StringRedisTemplate stringRedisTemplate;



    @GlobalTransactional//这个注解加在事务入口的方法上
    @Override
    public void makeOrder(OrderDto orderDto) {

        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        String userIdStr = request.getHeader("userId");
        //修改座位状态
        Integer count = cinemaFeignClient.batchUpdateSeatStatus(orderDto.getSeatIdList());
        if(count!=orderDto.getSeatIdList().size()){
            throw new CustomException(ExceptionCode.SEAT_ALREADY_SOLD);
        }

        //生成订单表数据
        Order order = new Order();

        Long orderId = Long.valueOf(orderDto.getOrderIdStr().substring(1));
        Long userId = Long.valueOf(userIdStr);
        HallMovie hallMovie = cinemaFeignClient.queryHallMovieById4Feign(orderDto.getHallMovieId());
        order.setOrderId(orderId);
        order.setOrderTotalDiscount(0);
        order.setUserId(userId);
        order.setOrderCount(orderDto.getSeatIdList().size());
        order.setOrderStatus(1);
        order.setOrderTime(new Date());
        order.setFareMoney(hallMovie.getFareMoney());
        order.setOrderTotal(order.getOrderCount()*order.getFareMoney());
        order.setOrderTotalPay(order.getOrderTotal());
        order.setHallMovieId(orderDto.getHallMovieId());
        order.setCinemaId(hallMovie.getCinemaId());
        order.setMovieId(hallMovie.getMovieId());

        orderMapper.insertSelective(order);

        for (Long seatId : orderDto.getSeatIdList()) {
            orderSeatMapper.insertSelective(new OrderSeat(orderId, seatId));
        }



        //把订单id发给rabbitmq，延时15分钟，等待用户支付
        //如果15分钟未支付，那么会自动取消订单
        rabbitTemplate.convertAndSend(UserConstants.DELAY_ORDER_EXCHANGE,
                UserConstants.DELAY_ORDER_ROUTING_KEY,
                orderId+"");

    }

    @Override
    public String queryOrderStatus(String orderId) {
        Long oid = Long.valueOf(orderId.substring(1));
        Order order = orderMapper.selectByPrimaryKey(oid);
        if(order.getOrderStatus()==2){
            return "paid";
        }
        return "notpaid";


    }

    @Override
    public void checkAvailableTicket(OrderDto orderDto) {
        //每一个能够买的座位id放入这个list
        List<Long> availableSeatList = new ArrayList<>();

        List<Long> seatIdList = orderDto.getSeatIdList();
        for (Long sid : seatIdList) {
            //使用setNx把座位id当做key，放入redis，如果放入失败，说明已经被其他人买掉了
            Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("order_ticket_" + sid, "1", 15, TimeUnit.MINUTES);
            if(flag){
                //买成功，把座位id加入到列表
                availableSeatList.add(sid);
            }else{
                //如果座位被其他人买掉了，那么自己已经买的座位全部释放
                for (Long sid2 : availableSeatList) {
                    stringRedisTemplate.delete("order_ticket_" + sid2);
                }
                throw new CustomException(ExceptionCode.SEAT_ALREADY_SOLD);
            }
        }
    }


    //财务系统收到钱后，发消息通知订单系统，改变订单状态为已经支付
    @RabbitListener(queues = {"order_pay_success_queue"})
    public void confirmPay(Message message, Channel channel){
        System.out.println("orderId = " + new String(message.getBody()));
        try {
            String orderIdStr = new String(message.getBody()).substring(1);
            Order order = new Order();
            order.setOrderId(Long.valueOf(orderIdStr));
            order.setOrderStatus(2);
            orderMapper.updateByPrimaryKeySelective(order);
            //手动ack
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }



    @GlobalTransactional
    //这里收到消息后，需要检查订单是否已经支付，如果支付就什么都不做
    //如果没支付，就取消订单
    @RabbitListener(queues = {UserConstants.DLX_ORDER_QUEUE})
    public void cancelOrder(Message message, Channel channel){
        System.out.println("orderId = " + new String(message.getBody()));
        try {
            String orderIdStr = new String(message.getBody());
            Long orderId = Long.valueOf(orderIdStr);
            //update tb_order set order_status = 3 where order_id = orderId and order_status = 1;
            Example example = new Example(Order.class);
            Example.Criteria criteria = example.createCriteria().andEqualTo("orderId", orderId).andEqualTo("orderStatus", 1);
           //使用example更新order_status属性
            Order order = new Order();
            order.setOrderStatus(3);
            int count = orderMapper.updateByExampleSelective(order, example);
            if(count==1){
                OrderSeat orderSeat = new OrderSeat();
                orderSeat.setOrderId(orderId);
                List<Long> seatIdList = orderSeatMapper.select(orderSeat).stream().map(s ->
                        s.getSeatId()
                ).collect(Collectors.toList());
                cinemaFeignClient.batchRevertSeatStatus(seatIdList);
            }

            //手动ack
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}
